import { TypescriptParser } from "typescript-parser";

type AST_NODE = any;
type CodeMap = Map<string, string>;
type InterfaceRelation = {
    origin: string;
    extendsFrom: string;
};
const parser = new TypescriptParser();

export const type2Object = async (input: string) => {
    // ts代码片段解析器
    const ast = await parser.parseSource(input);
    // 类型声明集合
    const node = JSON.parse(JSON.stringify(ast.declarations));
    console.info('node', node);
    // 存在类型声明，则遍历处理
    const props = node?.map((item: AST_NODE) => {
        const { name, properties } = item;
        console.info('node1', name);
        console.info('node2', properties);
        return {
            varName: name,
            properties: properties?.map((item: AST_NODE) => ({ name: item.name, type: item.type })),
        };
    });
    console.info('props', props);
    // props有值，则表示有interface
    const hasNoTsInterface = props.some((item: any) => !item.properties);
    if (hasNoTsInterface) {
        const errorMsg = {
            message: 'Only support to transform interface to object!'
        };
        throw errorMsg;
    }
    const interfaceRelation = getInterfaceRelation(input);
    console.info('12-' + interfaceRelation);
    const codeMap = getCodeMapByProp(props);
    console.info('13-' + codeMap);
    const newCodeMap = updateCodeMap(interfaceRelation, codeMap);
    console.info('14-' + newCodeMap);
    const code = generateCode(newCodeMap);
    return code;
};

const getInterfaceRelation = (input: string): InterfaceRelation[] => {
    const result: InterfaceRelation[] = [];
    const arr = input.slice().split(' ');
    // 是否存在继承
    for (let i = 0; i < arr.length; i++) {
        if (arr[i] === 'extends') {
            result.push({
                origin: arr[i - 1],
                extendsFrom: arr[i + 1]
            });
        }
    }
    return result;
};

const getCodeMapByProp = (props: AST_NODE[]) => {
    const map: CodeMap = new Map();
    let code = "";
    for (let prop of props) {
        code = "\n";
        const { varName, properties } = prop;
        code += `const EMPTY_${varName?.toUpperCase()} = {\n`;
        for (let i = 0; i < properties?.length; i++) {
            const { type, name } = properties[i];
            if (type?.toLowerCase() === 'string') {
                code += `\t${name}: '',\n`;
            } else if (type?.toLowerCase() === 'number') {
                code += `\t${name}: 0,\n`;
            } else if (type?.indexOf("{") > -1 && type?.indexOf("}") > -1) {
                // TODO: dfs?
                code += `\t${name}: ${type},\n`;
            } else if (type?.indexOf("[") > -1 && type?.indexOf("]") > -1) {
                code += `\t${name}: [],\n`;
            } else if (type?.toLowerCase() === "boolean" || type?.toLowerCase() === "false") {
                code += `\t${name}: false,\n`;
            } else if (type?.toLowerCase() === "true") {
                code += `\t${name}: true,\n`;
            } else {
                code += `\t${name}: {},\n`;
            }
        }
        code += "};";
        map.set(varName, code);
    }
    return map;
};

const updateCodeMap = (relation: InterfaceRelation[], codeMap: CodeMap): CodeMap => {
    const newMap = new Map<string, string>(codeMap);
    newMap.forEach((_, key) => {
        // some方法用于检测数组中的元素是否满足return的判断条件，如果有一个元素满足条件，则表达式返回true , 剩余的元素不会再执行检测
        if (relation.some(item => item.origin === key)) {
            const extendInterface = relation.find(item => item.origin === key)?.extendsFrom || '';
            const originCode = newMap.get(key) || '';
            const extendCode = newMap.get(extendInterface) || '';
            const originIndex = originCode?.indexOf('}') || -1;
            const extendIndex = extendCode?.indexOf('{') || -1;
            const newCode = (!!extendCode ? originCode?.slice(0, originIndex) : originCode) + (extendIndex < 0 ? '' : extendCode?.slice(extendIndex + 1));
            newMap.set(key, newCode);
        }
    });
    return newMap;
};

const generateCode = (map: CodeMap): string => {
    let code = '';
    map.forEach((value) => {
        code += `${value}\n`;
    });
    return code;
};